package data

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"time"

	"github.com/golang-jwt/jwt/v4"
	"gorm.io/gorm"

	"gitee.com/architect-training/substitute-driver/customer-srv/internal/biz"
)

const (
	VeriCodeKeyPrefix = "customer:veri-code:"
)

type CustomerData struct {
	data *Data
}

func NewCustomerData(data *Data) *CustomerData {
	return &CustomerData{data: data}
}

// SetVeriCode 设置验证码
func (c *CustomerData) SetVeriCode(phone, code string, ex int64) error {
	ctx := context.Background()
	key := VeriCodeKeyPrefix + phone
	cmd := c.data.rdb.SetEx(ctx, key, code, time.Duration(ex)*time.Minute)
	if _, err := cmd.Result(); err != nil {
		return err
	}
	return nil
}

// GetVerifyCode 根据手机号码获取验证码
func (c *CustomerData) GetVerifyCode(phone string) string {
	ctx := context.Background()
	key := VeriCodeKeyPrefix + phone
	status := c.data.rdb.Get(ctx, key)
	code, _ := status.Result()
	return code
}

// GetCustomerByTelephone 根据手机号码获取验证码
func (c *CustomerData) GetCustomerByTelephone(telephone string) (*biz.Customer, error) {
	cus := &biz.Customer{}
	res := c.data.mdb.Where("telephone = ?", telephone).First(cus)
	if res.Error == nil && cus.ID > 0 {
		return cus, nil
	}

	// 记录不存在
	if res.Error != nil && errors.Is(res.Error, gorm.ErrRecordNotFound) {
		cus.Telephone = telephone
		cus.Name = sql.NullString{Valid: false}
		cus.Email = sql.NullString{Valid: false}
		cus.Wechat = sql.NullString{Valid: false}
		res1 := c.data.mdb.Create(cus)
		if res1.Error == nil {
			return cus, nil
		} else {
			return nil, res1.Error
		}
	}

	return cus, res.Error
}

// GenerateTokenAndSave 生成 Token 并保存
func (c *CustomerData) GenerateTokenAndSave(customer *biz.Customer, duration time.Duration, secret string) (string, error) {
	// 生成 Token
	claims := jwt.RegisteredClaims{
		// 签发方，签发机构
		Issuer: "National Security Agency",
		// 说明
		Subject: "customer-authentication",
		// 签发给谁使用
		Audience: []string{"customer", "other"},
		// 有效期至
		ExpiresAt: jwt.NewNumericDate(time.Now().Add(duration)),
		// 何时启用
		NotBefore: nil,
		// 签发时间
		IssuedAt: jwt.NewNumericDate(time.Now()),
		// ID: 用户的 ID
		ID: fmt.Sprintf("%d", customer.ID),
	}

	// 生成 token
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	// 签名 token
	signedToken, err := token.SignedString([]byte(secret))
	if err != nil {
		return "", err
	}

	customer.Token = signedToken
	customer.TokenCreatedAt = sql.NullTime{
		Time:  time.Now(),
		Valid: true,
	}

	if res := c.data.mdb.Save(customer); res.Error != nil {
		return "", res.Error
	}

	return signedToken, nil
}

func (c *CustomerData) GetToken(id string) (string, error) {
	cus := &biz.Customer{}
	if res := c.data.mdb.First(cus, id); res.Error != nil {
		return "", res.Error
	}
	return cus.Token, nil
}

func (c *CustomerData) DelToken(id string) error {
	cus := &biz.Customer{}
	if result := c.data.mdb.First(cus, id); result.Error != nil {
		return result.Error
	}
	cus.Token = ""
	cus.TokenCreatedAt = sql.NullTime{Valid: false}
	err := c.data.mdb.Save(cus).Error
	return err
}
